#include "TimedAnimation.h"

//=============================================================================
TimedAnimation::TimedAnimation (void* animTarget, const String& name, const String& type)
:	Animation (animTarget, name, type)
{
	looping = false;
	pingPong = false;
	forwards = true;
	duration = 500;
	loopCount = 0;
	numLoops = 0;
	pos = 0;
	speed = 1.0;
	currentProgress = 0.0;
}

TimedAnimation::~TimedAnimation ()
{
}

void TimedAnimation::setSpeed (double newSpeed)
{
	speed = newSpeed;
}

void TimedAnimation::setDuration (int ms)
{
	duration = ms;
}

void TimedAnimation::setLooping (bool shouldLoop, bool shouldPingPong, int numTimes)
{
	looping = shouldLoop;
	if (looping) pingPong = shouldPingPong;
	else pingPong = false;
	numLoops = numTimes;
}

bool TimedAnimation::isLooping ()
{
	return looping;
}

bool TimedAnimation::isPingPong ()
{
	return pingPong;
}

int TimedAnimation::getDuration ()
{
	return duration;
}

double TimedAnimation::getSpeed ()
{
	return speed;
}

bool TimedAnimation::useTimeSlice (const int elapsed)
{
	int newPos = pos;

	if (forwards)	// Going forwards.... ---------------------------
	{	
		// We are going forwards, 
		// so add the elapsed time...
		newPos += elapsed;

		if (newPos >= duration)
		{
			// we have reached/passed the end...
			if (looping)
			{
				// Work out how much we've overshot...
				int overshoot = newPos - duration;
				if (pingPong)
				{
					// In ping-pong mode, the overshoot
					// should be taken back from the
					// duration to find our new position...
					newPos = duration - overshoot;
					// ... and we must make sure we're
					// going backwards next time...
					forwards = false;
					// [In ping-pong mode, a 'loop' ends
					//  when it arrives back at the start.]
				}
				else
				{
					// See if we've looped enough times...
					if (loopCount >= numLoops && numLoops > 0)
					{
						skipToEnd ();
						return false;
					}
					// In normal loop mode, the overshoot
					// wraps back to zero...
					newPos = overshoot;
					loopCount++;
				}
			}
			else
			{
				// We're not looping, so passing the
				// end requires us to stop...
				skipToEnd ();
				return false;
			}
		}
		else
		{
			// We are within the duration, so we don't
			// need to do anything special...
		}
	}
	else		// Going backwards.... ---------------------------
	{
		// We are going backwards... we must
		// subtract the elapsed time...
		newPos -= elapsed;

		if (newPos <= 0)
		{
			// We have passed the beginning...
			// Check if we've looped enough times...
			if (++loopCount >= numLoops && numLoops > 0)
			{
				resetToStart ();
				return false;
			}
			newPos = -newPos;
			forwards = true;
		}
	}

	pos = newPos;
	
	currentProgress = pos / (double) duration;
	update (currentProgress);
	return true;
}

void TimedAnimation::skipToEnd ()
{
	update (1.0);
}

void TimedAnimation::resetToStart ()
{
	update (0.0);
}

//=============================================================================
LinearXYAnimation::LinearXYAnimation (void* animTarget, const String& name, const String& type, int startX, int startY, int finishX, int finishY, int durationMs)
:	TimedAnimation (animTarget,name,type)
{
	x2 = finishX;
	y2 = finishY;
	x1 = startX;
	y1 = startY;
	setDuration (durationMs);
}

LinearXYAnimation::~LinearXYAnimation ()
{
}

void LinearXYAnimation::update (double progress)
{
	int x = roundDoubleToInt (x1 + (progress * (x2 - x1)));
	int y = roundDoubleToInt (y1 + (progress * (y2 - y1)));
	applyUpdateToTarget (x,y);
}

//=============================================================================
LinearDoubleAnimation::LinearDoubleAnimation (void* animTarget, const String& name, const String& type, double start, double finish, int durationMs)
:	TimedAnimation (animTarget,name,type)
{
	vstart = start;
	vend = finish;
	setDuration (durationMs);
}

LinearDoubleAnimation::~LinearDoubleAnimation ()
{
}

void LinearDoubleAnimation::update (double progress)
{
	double v = vstart + (progress * (vend - vstart));
	applyUpdateToTarget (v);
}

//=============================================================================
LinearColourAnimation::LinearColourAnimation (void* animTarget, const String& name, const String& type, const Colour& startCol, const Colour& endCol, int durMs)
:	TimedAnimation (animTarget,name,type)
{
	c1 = startCol;
	c2 = endCol;
	setDuration (durMs);
}

LinearColourAnimation::~LinearColourAnimation ()
{
}

void LinearColourAnimation::update (double progress)
{
	uint8 r = roundDoubleToInt (c1.getRed() + (progress * (c2.getRed() - c1.getRed())));
	uint8 g = roundDoubleToInt (c1.getGreen() + (progress * (c2.getGreen() - c1.getGreen())));
	uint8 b = roundDoubleToInt (c1.getBlue() + (progress * (c2.getBlue() - c1.getBlue())));
	uint8 a = roundDoubleToInt (c1.getAlpha() + (progress * (c2.getAlpha() - c1.getAlpha())));
	applyUpdateToTarget (Colour (r,g,b,a));
}